if _ACTION == 'ninja' then
    require('ninja')
end

if _ACTION == 'export-compile-commands' then
    require('export-compile-commands')
end

workspace('rive')

newoption({
    trigger = 'config',
    description = 'one-and-only config (for multiple configs, target a new --out directory)',
    allowed = { { 'debug' }, { 'release' }, { nil } },
    default = nil,
})
newoption({ trigger = 'release', description = 'shortcut for \'--config=release\'' })
if _OPTIONS['release'] then
    if _OPTIONS['config'] then
        error('use either \'--release\' or \'--config=release/debug\' (not both)')
    end
    _OPTIONS['config'] = 'release'
    _OPTIONS['release'] = nil
elseif not _OPTIONS['config'] then
    _OPTIONS['config'] = 'debug'
end
RIVE_BUILD_CONFIG = _OPTIONS['config']

newoption({
    trigger = 'out',
    description = 'Directory to generate build files',
    default = nil,
})
RIVE_BUILD_OUT = _WORKING_DIR .. '/' .. (_OPTIONS['out'] or ('out/' .. RIVE_BUILD_CONFIG))

newoption({
    trigger = 'toolset',
    value = 'type',
    description = 'Choose which toolchain to build with',
    allowed = {
        { 'clang', 'Build with Clang' },
    },
    default = 'clang',
})

newoption({
    trigger = 'arch',
    value = 'ABI',
    description = 'The ABI with the right toolchain for this build, generally with Android',
    allowed = {
        { 'host' },
        { 'x86' },
        { 'x64' },
        { 'arm' },
        { 'arm64' },
        { 'ohos' },
        { 'universal', '"fat" library on apple platforms' },
        { 'wasm', 'emscripten targeting web assembly' },
        { 'js', 'emscripten targeting javascript' },
    },
    default = 'host',
})

newoption({
    trigger = 'for_ohos',
    description = 'compile for OpenHarmony (uses same infrastructure as android)',
})

newoption({
    trigger = 'ohos_api',
    description = 'Target Android API version number',
    default = '18',
})

newoption({
    trigger = 'with-rtti',
    description = 'don\'t disable rtti (nonstandard for Rive)',
})

newoption({
    trigger = 'with-pic',
    description = 'enable position independent code',
})

newoption({
    trigger = 'with-exceptions',
    description = 'don\'t disable exceptions (nonstandard for Rive)',
})

newoption({
    trigger = 'no-lto',
    description = 'Don\'t build with link time optimizations.',
})

newoption({
    trigger = 'for_unreal',
    description = 'compile for unreal engine',
})

newoption({
    trigger = 'with-asan',
    description = 'enable AddressSanitizer',
})

newoption({
    trigger = 'with_ubsan',
    description = 'enable UndefinedBehaviorSanitizer',
})

newoption({ trigger = 'with_optick', description = 'use optick profiler' })
if _OPTIONS['with_optick'] then
    defines({ 'RIVE_OPTICK' })
    RIVE_OPTICK_URL = 'bombomby/optick'
    RIVE_OPTICK_VERSION = '1.4.0.0'
end 

location(RIVE_BUILD_OUT)
targetdir(RIVE_BUILD_OUT)
objdir(RIVE_BUILD_OUT .. '/obj')
toolset(_OPTIONS['toolset'] or 'clang')
language('C++')
cppdialect('C++17')
configurations({ 'default' })
filter({ 'options:not with-rtti' })
rtti('Off')
filter({ 'options:with-rtti' })
rtti('On')
filter({ 'options:not with-exceptions' })
exceptionhandling('Off')
filter({ 'options:with-exceptions' })
exceptionhandling('On')

filter({ 'options:with-asan' })
do
    sanitize({ 'Address' })

    -- Edit & continue and incremental link are not compatible with ASAN in MSVC.
    editandcontinue('Off')
    flags({ 'NoIncrementalLink' })
end

filter({ 'options:with_ubsan', 'system:not windows' })
do
    buildoptions({
        '-fsanitize=undefined',
        '-fno-sanitize-recover=all'
    })
    linkoptions({
        '-fsanitize=undefined',
        '-fno-sanitize-recover=all'
    })
end

filter({ 'options:with-pic' })
do
    pic('on')
    buildoptions({ '-fPIC' })
    linkoptions({ '-fPIC' })
end

filter('options:config=debug')
do
    defines({ 'DEBUG' })
    symbols('On')
end

filter('options:config=release')
do
    defines({ 'RELEASE' })
    defines({ 'NDEBUG' })
    optimize('On')
end

filter({ 'options:config=release', 'options:not no-lto', 'system:not macosx', 'system:not ios' })
do
    if linktimeoptimization then
        linktimeoptimization('On')
    else
        -- Deprecated way of turning on LTO, for older versions of premake.
        flags({ 'LinkTimeOptimization' })
    end
end

filter({ 'options:config=release', 'options:not no-lto', 'system:macosx or ios' })
do
    -- The 'linktimeoptimization' command attempts to use llvm-ar, which doesn't always exist on macos.
    buildoptions({ '-flto=full' })
    linkoptions({ '-flto=full' })
end

newoption({
    trigger = 'windows_runtime',
    description = 'Choose whether to use staticruntime on/off/default',
    allowed = {
        { 'default', 'Use default runtime' },
        { 'static', 'Use static runtime' },
        { 'dynamic', 'Use dynamic runtime' },
        { 'dynamic_debug', 'Use dynamic runtime force debug' },
        { 'dynamic_release', 'Use dynamic runtime force release' },
    },
    default = 'default',
})

newoption({
    trigger = 'toolsversion',
    value = 'msvc_toolsversion',
    description = 'specify the version of the compiler tool. On windows thats the msvc version which affects both clang and msvc outputs.',
    default = 'latest',
})

-- This is just to match our old windows config. Rive Native specifically sets
-- static/dynamic and maybe we should do the same elsewhere.
filter({ 'system:windows', 'options:windows_runtime=default', 'options:not for_unreal' })
do
    staticruntime('on') -- Match Skia's /MT flag for link compatibility
    runtime('Release')
end

filter({ 'system:windows', 'options:windows_runtime=static', 'options:not for_unreal' })
do
    staticruntime('on') -- Match Skia's /MT flag for link compatibility
end

filter({ 'system:windows', 'options:windows_runtime=dynamic', 'options:not for_unreal' })
do
    staticruntime('off')
end

filter({
    'system:windows',
    'options:not windows_runtime=default',
    'options:config=debug',
    'options:not for_unreal',
})
do
    runtime('Debug')
end

filter({
    'system:windows',
    'options:not windows_runtime=default',
    'options:config=release',
    'options:not for_unreal',
})
do
    runtime('Release')
end

filter({ 'system:windows', 'options:windows_runtime=dynamic_debug', 'options:not for_unreal' })
do
    staticruntime('off')
    runtime('Debug')
end

filter({ 'system:windows', 'options:windows_runtime=dynamic_release', 'options:not for_unreal' })
do
    staticruntime('off')
    runtime('Release')
end

filter('system:windows')
do
    architecture('x64')
    defines({ '_USE_MATH_DEFINES', 'NOMINMAX' })
end

filter({ 'system:windows', 'options:for_unreal' })
do
    staticruntime('off')
    runtime('Release')
    -- Unreal no longer supports c++17
    cppdialect('C++20')
end

-- Unreal "prefers" certain msvc versions so we try to match it from the python build script by using "toolsversion" option
-- this defaults to "latest" which will just do the default behavior anyway. So no need for a check
toolsversion(_OPTIONS['toolsversion'])

filter({ 'system:windows', 'options:toolset=clang' })
do
    buildoptions({
        '-Wno-c++98-compat',
        '-Wno-c++20-compat',
        '-Wno-c++98-compat-pedantic',
        '-Wno-c99-extensions',
        '-Wno-ctad-maybe-unsupported',
        '-Wno-deprecated-copy-with-user-provided-dtor',
        '-Wno-deprecated-declarations',
        '-Wno-documentation',
        '-Wno-documentation-pedantic',
        '-Wno-documentation-unknown-command',
        '-Wno-double-promotion',
        '-Wno-exit-time-destructors',
        '-Wno-float-equal',
        '-Wno-global-constructors',
        '-Wno-implicit-float-conversion',
        '-Wno-newline-eof',
        '-Wno-old-style-cast',
        '-Wno-reserved-identifier',
        '-Wno-shadow',
        '-Wno-sign-compare',
        '-Wno-sign-conversion',
        '-Wno-unused-macros',
        '-Wno-unused-parameter',
        '-Wno-four-char-constants',
        '-Wno-unreachable-code',
        '-Wno-switch-enum',
        '-Wno-missing-field-initializers',
        '-Wno-unsafe-buffer-usage',
    })
end

filter({})

-- building for harmony.
if _OPTIONS['for_ohos'] or _ARGS[1] == 'ohos' then
    -- premake.api.addAllowed("system", "ohos")
    system('android')
    pic('on') -- Position-independent code is required for NDK libraries.

    -- Detect the OHOS Native SDK
    local OHOS_SDK_PATH = os.getenv('OHOS_NDK') or '<undefined>'
    if OHOS_SDK_PATH == '<undefined>' then
        error('OpenHarmony Native SDK path not found. Please set OHOS_NDK environment variable.')
    end

    -- OpenHarmony 工具链配置
    local ohos_toolchain = OHOS_SDK_PATH .. '/llvm'
    local ohos_sysroot = OHOS_SDK_PATH .. '/sysroot'

    -- 根据架构设置目标
    local target_arch = ''
    if _OPTIONS['arch'] == 'arm64' then
        target_arch = 'aarch64-unknown-linux-ohos'
    elseif _OPTIONS['arch'] == 'arm' then
        target_arch = 'armv7-unknown-linux-ohos'
    elseif _OPTIONS['arch'] == 'x64' then
        target_arch = 'x86_64-unknown-linux-ohos'
    else
        target_arch = 'x86_64-unknown-linux-ohos'  -- 默认
    end

    -- Windows requires extensions for executables
    local exe_ext = ''
     if os.host() == 'windows' then
         exe_ext = '.exe'
     end

    -- 完整的工具链路径配置
    local ohos_tools = {
       cc = ohos_toolchain .. '/bin/' .. 'clang' .. exe_ext,
       cxx = ohos_toolchain .. '/bin/' .. 'clang++' .. exe_ext,
       ar = ohos_toolchain .. '/bin/llvm-ar.exe',
    }

    -- 验证工具是否存在
    for tool_name, tool_path in pairs(ohos_tools) do
        local f = io.open(tool_path, 'r')
        if f then
            f:close()
            print("Found " .. tool_name .. " at: " .. tool_path)
        else
            print("ERROR: " .. tool_name .. " not found at: " .. tool_path)
            error("OpenHarmony toolchain not found: " .. tool_path)
        end
    end

    premake.tools.ohos_ndk = {}

    for k, v in pairs(premake.tools.clang) do
        premake.tools.ohos_ndk[k] = v
    end

    function premake.tools.ohos_ndk.gettoolname(cfg, tool)
        return ohos_tools[tool]
    end

    local premake_valid_tools = premake.action._list[_ACTION].valid_tools
    if premake_valid_tools ~= nil then
        if not premake_valid_tools['cc'] then
            premake_valid_tools['cc'] = {}
        end
        table.insert(premake_valid_tools['cc'], 'ohos_ndk')
    end

    toolset('ohos_ndk')

    buildoptions({
        '--target=x86_64-linux-ohos',
        '--sysroot=' .. ohos_sysroot,
        '-fdata-sections',
        '-ffunction-sections',
        '-funwind-tables',
        '-fstack-protector-strong',
        '-no-canonical-prefixes',
        '-D__MUSL__',
    })

    linkoptions({
        '--target=x86_64-linux-ohos',
        '--sysroot=' .. ohos_sysroot,
        '-fdata-sections',
        '-ffunction-sections',
        '-funwind-tables',
        '-fstack-protector-strong',
        '-no-canonical-prefixes',
        '-Wl,--fatal-warnings',
        '-Wl,--gc-sections',
        '-Wl,--no-undefined',
        '-static-libstdc++',
    })

    defines({
        'RIVE_OHOS',
        'RIVE_ANDROID=0',  -- 明确禁用 Android 相关定义
    })

    filter({})
end

filter('system:linux', 'options:arch=x64')
do
    architecture('x64')
end

filter('system:linux', 'options:arch=arm')
do
    architecture('arm')
end

filter('system:linux', 'options:arch=arm64')
do
    architecture('arm64')
end

filter({})
